// /////////////////////////////////////////////////////////////////////////////
//
// Name:            SkyBoxComponent.cpp
// Author:          Michael Bartsch (ZCCdark203)
//
// Desc :           Component wrapper of Irrlicht's SceneManager function
//                  addSkyBoxSceneNode(...).
//
// License:         Copyright (C) 2009 Michael Bartsch and Contributors
//
//                  This program is free software: you can redistribute it
//                  and/or modify it under the terms of the zlib/libpng License.
//                  See main.cpp for conditions of distribution and use.
//
// /////////////////////////////////////////////////////////////////////////////

// Include files
#include "SkyBoxComponent.h"
#include "../../core/GameManager.h"


// SkyBoxComponent class
// SkyBoxComponent constructor (default).
SkyBoxComponent::SkyBoxComponent(Entity *parent)
: SceneComponent(parent, true)
{
    if(pParent != NULL)
      init();
}

// SkyBoxComponent constructor.
SkyBoxComponent::SkyBoxComponent(Entity *parent, ITexture *top, ITexture *bottom, ITexture *left,
                                 ITexture *right, ITexture *front, ITexture *back)
: SceneComponent(parent, true)
{
    if(pParent != NULL)
    {
       init();
       setTextures(top, bottom, left, right, front, back);
    }
}

// SkyBoxComponent constructor.
SkyBoxComponent::SkyBoxComponent(Entity *parent, const std::string &top, const std::string &bottom,
                                 const std::string &left, const std::string &right,
                                 const std::string &front, const std::string &back)
: SceneComponent(parent, true)
{
    if(pParent != NULL)
    {
       init();
       setTextures(top, bottom, left, right, front, back);
    }
}

// SkyBoxComponent deconstructor.
SkyBoxComponent::~SkyBoxComponent()
{
}

// Common initialisation method.
void SkyBoxComponent::init()
{
    // Create the internal scene node.
    mSceneNode = GameManager::Instance()->getSceneManager()->addSkyBoxSceneNode(0, 0, 0, 0, 0, 0, 0,
                                                                                pParent->getID());

    // Set-up internal mechanism for collision detection.
    mTriSelector = NULL;
    mMetaSelector = NULL;
}

// Loads and sets the textures of the sky box.
void SkyBoxComponent::setTextures(const std::string &top, const std::string &bottom,
                                  const std::string &left, const std::string &right,
                                  const std::string &front, const std::string &back,
                                  bool ignoreNull)
{
    if( (front != "") || !ignoreNull)
      setTexture(0, front);

    if( (left != "") || !ignoreNull)
      setTexture(1, left);

    if( (back != "") || !ignoreNull)
      setTexture(2, back);

    if( (right != "") || !ignoreNull)
      setTexture(3, right);

    if( (top != "") || !ignoreNull)
      setTexture(4, top);

    if( (bottom != "") || !ignoreNull)
      setTexture(5, bottom);
}

// Sets the textures of the sky box.
void SkyBoxComponent::setTextures(ITexture *top, ITexture *bottom, ITexture *left, ITexture *right,
                                  ITexture *front, ITexture *back, bool ignoreNull)
{
    if(front || !ignoreNull)
      setTexture(0, front);

    if(left || !ignoreNull)
      setTexture(1, left);

    if(back || !ignoreNull)
      setTexture(2, back);

    if(right || !ignoreNull)
      setTexture(3, right);

    if(top || !ignoreNull)
      setTexture(4, top);

    if(bottom || !ignoreNull)
      setTexture(5, bottom);
}

// Sets the texture of the appropriate side based on the filename.
void SkyBoxComponent::setTexture(u32 side, const std::string &fileName)
{
    // Check if we got an reasonable side.
    if(side >= 6)
      return;

    // Set up variables.
    AssetGroup *assets = pParent->getAssetGroup();
    ITexture *texture = 0;

    // Retrieve pointer to the texture.
    if(assets != NULL)
    {
        // Unsubscribe from previous asset.
        assets->disconnectEventSignal(std::string("textures/") + mTextureFileNames[side], this);

        // Get texture.
        TextureProcessor *processor = static_cast<TextureProcessor*>
                                      (assets->getAssetProcessor("textures"));
        texture = processor->getTexture(fileName);

        if(texture)
        {
           assets->connectEventSignal(std::string("textures/") + fileName, this,
                                            &SkyBoxComponent::onTextureSkyBox);
           mTextureFileNames[side] = fileName;
        }
    }

    else
      texture = GameManager::Instance()->getDriver()->getTexture(fileName.c_str());

    // Set texture.
    mSceneNode->getMaterial(side).setTexture(0, texture);
}

// Sets the texture of the appropriate side.
void SkyBoxComponent::setTexture(u32 side, ITexture *texture)
{
    // Set up variables.
    AssetGroup *assets = pParent->getAssetGroup();

    // Check if we need to unsubscribe from an asset group texture.
    if(assets != NULL)
    {
       assets->disconnectEventSignal(std::string("textures/") + mTextureFileNames[side], this);
       mTextureFileNames[side] = "";
    }

    // Set texture.
    mSceneNode->getMaterial(side).setTexture(0, texture);
}

// Responds to changes of a skybox texture attached to this component.
void SkyBoxComponent::onTextureSkyBox(void *p)
{
    // Get texture data.
    ITexture *texture = reinterpret_cast<ITexture*>(p);

    // Determine layer.
    std::string fileName = (GameManager::Instance()->getDevice()->getFileSystem()->
                              getFileBasename(texture->getName())).c_str();

    u32 side = 6;

    for(u32 i = 0; i < 6; i++)
    {
        if(mTextureFileNames[i] == fileName)
        {
           side = i;
           break;
        }
    }

    if(side == 6)
      return;

    // Set texture.
    mSceneNode->getMaterial(side).setTexture(0, texture);
}

// Serializes this object.
bool SkyBoxComponent::onSerialization(SerializedAttributes *attributes)
{
    // Create a root.
    SerializedAttributes *root = new SerializedAttributes();

    // Serialize SceneComponent elements.
    SceneComponent::onSerialization(root);

    // Serialize attributes.
    // Serialize textures.
    SerializedAttributes *textures = new SerializedAttributes();

    textures->pushString("top", mTextureFileNames[4]);
    textures->pushString("bottom", mTextureFileNames[5]);
    textures->pushString("left", mTextureFileNames[1]);
    textures->pushString("right", mTextureFileNames[3]);
    textures->pushString("front", mTextureFileNames[0]);
    textures->pushString("back", mTextureFileNames[2]);

    root->pushObject("textures", textures);
    textures->drop();

    // Add root to the given collection of attributes.
    attributes->pushObject("SkyBoxComponent", root);
    root->drop();

    // Return
    return true;
}

// Deserializes this object.
bool SkyBoxComponent::onDeserialization(SerializedAttributes *attributes)
{
    // Retrieve root attribute from the collection.
    SerializedAttribute *rootAttribute = attributes->pullAttribute();

    if(rootAttribute == NULL || rootAttribute->getName() != "SkyBoxComponent")
      return false;

    SerializedAttributes *root = reinterpret_cast<SerializedAttributes*>(rootAttribute->getObject());
    root->grab();

    rootAttribute->drop(); // We no longer need this attribute.

    // Process all attributes in the root collection.
    for(SerializedAttribute *attribute = root->pullAttribute(); attribute != NULL; attribute = root->pullAttribute())
    {
       // Deserialize attributes
       if(attribute->getName() == "SceneComponent")
       {
           // Rewrap attribute so that it can be deserialized properly.
           SerializedAttributes *wrappedAttribute = new SerializedAttributes();
           wrappedAttribute->pushAttribute(attribute);

           // Deserialize the component.
           SceneComponent::onDeserialization(wrappedAttribute);

           // Clean up.
           wrappedAttribute->drop();
       }

       else if(attribute->getName() == "textures")
       {
          // Convert to a collection of attributes.
          SerializedAttributes *textures = reinterpret_cast<SerializedAttributes*>(attribute->getObject());

          // Predeclaration of needed variables.
          std::string top, bottom, left, right, front, back;

          // Process all attributes in the textures collection.
          for(SerializedAttribute *innerAttribute = textures->pullAttribute(); innerAttribute != NULL; innerAttribute = textures->pullAttribute())
          {
                  if(attribute->getName() == "top")      top = innerAttribute->getString();
             else if(attribute->getName() == "bottom")   bottom = innerAttribute->getString();
             else if(attribute->getName() == "left")     left = innerAttribute->getString();
             else if(attribute->getName() == "right")    right = innerAttribute->getString();
             else if(attribute->getName() == "front")    front = innerAttribute->getString();
             else if(attribute->getName() == "back")     back = innerAttribute->getString();
          }

          setTextures(top, bottom, left, right, front, back, false);
       }

       // We no longer need a reference to the attribute.
       attribute->drop();
    }

    // We no longer need a reference to the root.
    root->drop();

    // We're done.
    return true;
}

// End of File
